﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Mtool.FSM;
using MToolStudio.Render;
using MToolStudio.UI;

namespace MToolStudio
{
    public partial class MtoolMainForm : Form
    {
        //private UITool addStateTool;
        //private UITool addActionTool;
        //private UITool defaultTool;

        public MtoolMainForm()
        {
            InitializeComponent();
        }

        private void button_State_Click(object sender, EventArgs e)
        {
            fsmDesigner.CurrentTool = new AddStateTool(fsmDesigner, Microsoft.Mtool.FSM.StateType.Free);
        }

        private void button_Action_Click(object sender, EventArgs e)
        {
            fsmDesigner.CurrentTool = new AddActionTool(fsmDesigner);
        }

        private void MtoolMainForm_Load(object sender, EventArgs e)
        {
            //addStateTool = new AddStateTool(fsmDesigner, Microsoft.Mtool.FSM.StateType.Entry);
            //addActionTool = new AddActionTool(fsmDesigner, Microsoft.Mtool.FSM.ActionType.Control);
            //defaultTool = new DefaultActionTool(fsmDesigner);
            Logger.Instance.OnLogGrown += new LogChangeHandler(Instance_OnLogGrown);
            
            fsmDesigner.OnCurrentUIToolChanged += new EventHandler<UIToolEventArgs>(fsmDesigner_OnCurrentUIToolChanged);
            fsmDesigner.CurrentTool = new DefaultActionTool(fsmDesigner);
            fsmDesigner.OnFocusObjectChanged += new EventHandler<UIFocusChangedEventArgs>(fsmDesigner_OnFocusObjectChanged);
            fsmDesigner.OnCharInputToFocusedObject += new EventHandler<KeyEventArgs>(fsmDesigner_OnCharInputFocusedObject);
            LoadTokensToTreeView();
            
            variableEditor_below.InitPict();
            
            //variableEditor_below.InitPict(ConfigurationManager.AppSettings["PictBinFile"], ConfigurationManager.AppSettings["PictWorkFolder"]);
        }

        void Instance_OnLogGrown(string logContent)
        {
            //need better method to do this!

            //textBox_Output.Text += logContent;
        }

        void fsmDesigner_OnFocusObjectChanged(object sender, UIFocusChangedEventArgs e)
        {
            textBox_ActionInput.Text = "";
            textBox_ActionOutput.Text = "";
            propertyGrid_RenderItem.SelectedObject = null;
            listBox_tokenSeq.SelectedItem = null;
            listBox_tokenSeq.Items.Clear();

            if (e.Focus == null)
            {
                return;
            }
            
            if (e.Focus is IActionRenderable)
            {
                IActionRenderable ar = e.Focus as IActionRenderable;

                //propertygrid
                propertyGrid_RenderItem.SelectedObject = ar.InnerAction;

                //TODO
                //tokenseq
                if (ar.InnerAction.Stubs != null)
                {
                    foreach (ActionImpl ai in ar.InnerAction.Stubs)
                    {
                        listBox_tokenSeq.Items.Add(ai);
                    }
                }
                //tokendetail
                if (listBox_tokenSeq.Items.Count > 0)
                {
                    listBox_tokenSeq.SelectedIndex = 0;
                }
            }
            else if (e.Focus is StateR)
            {
                StateR sr = e.Focus as StateR;
                propertyGrid_RenderItem.SelectedObject = sr.InnerState;
            }
        }

        void fsmDesigner_OnCurrentUIToolChanged(object sender, UIToolEventArgs e)
        {           
            button_Action.FlatStyle = FlatStyle.Standard;
            button_Default.FlatStyle = FlatStyle.Standard;
            button_State.FlatStyle = FlatStyle.Standard;
            if (e.Tool is DefaultActionTool)
                button_Default.FlatStyle = FlatStyle.Flat;
            else if (e.Tool is AddActionTool)
                button_Action.FlatStyle = FlatStyle.Flat;
            else if (e.Tool is AddStateTool)
                button_State.FlatStyle = FlatStyle.Flat;
        }

        private TreeNode tokenRoot;
        private TreeNode funcRoot;

        private void LoadTokensToTreeView()
        {
            //initialize the root
            tokenRoot = treeView_Tokens.Nodes.Add("TokenRoot", "Tokens");
            tokenRoot.Tag = typeof(TokenImpl);
            funcRoot = treeView_Tokens.Nodes.Add("FuncRoot", "Functions");
            funcRoot.Tag = typeof(FunctionImpl);
            //load the tokens
            /*Dictionary<string, List<string>> tokenInfo = fsmDesigner.Machine.TokenMgr.Init(
                System.Configuration.ConfigurationManager.AppSettings["TokenFolder"],
                System.Configuration.ConfigurationManager.AppSettings["TharnFolder"]);

            foreach (string key in tokenInfo.Keys)
            {
                List<string> tokenNames = tokenInfo[key];
                TreeNode tokenNode = tokenRoot.Nodes.Add(key, key);
                foreach (string singleToken in tokenNames)
                {
                    TreeNode singleTokenNode = new TreeNode(singleToken);
                    singleTokenNode.Tag = ActionTreeViewItemType.Suitep2TokenCall;
                    tokenNode.Nodes.Add(singleTokenNode);
                }
            }
            tokenRoot.Expand();*/
        }

        private string tmpSaveFilePath = "fsm.tmp.ui.xml";

        private void tabControl_Main_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (tabControl_Main.SelectedTab == tabPage_Source)
            {
                fsmDesigner.Machine.SaveToXML(tmpSaveFilePath);
                using (System.IO.StreamReader rder = new System.IO.StreamReader(tmpSaveFilePath))
                {
                    textBox_FSMSrc.Text = rder.ReadToEnd();
                }
            }
        }

        private void button_Default_Click(object sender, EventArgs e)
        {
            fsmDesigner.CurrentTool = new DefaultActionTool(fsmDesigner);
        }

        private void treeView_Tokens_ItemDrag(object sender, ItemDragEventArgs e)
        {
            TreeNode node = e.Item as TreeNode;
            if(node.Tag != null)
            {
                if (node.Parent == tokenRoot)
                {
                    fsmDesigner.CurrentTool = new ActionImplApplyTool(fsmDesigner, ActionTreeViewItemType.TokenCall, node.Tag as TokenImpl);
                }
                else if (node.Parent == funcRoot)
                {
                    fsmDesigner.CurrentTool = new ActionImplApplyTool(fsmDesigner, ActionTreeViewItemType.FunctionCall, node.Tag as FunctionImpl);
                }
            }
        }

        private void treeView_Tokens_DragDrop(object sender, DragEventArgs e)
        {

        }

        private void TryToSaveActionInfo(bool isSave)
        {
            string errorMsg = null;
            if (listBox_tokenSeq.SelectedItem is TokenImpl)
            {
                errorMsg = (listBox_tokenSeq.SelectedItem as TokenImpl).Apply(textBox_ActionInput.Text, textBox_ActionOutput.Text, isSave);
            }
            else if (listBox_tokenSeq.SelectedItem is FunctionImpl)
            {
                errorMsg = (listBox_tokenSeq.SelectedItem as FunctionImpl).Apply(textBox_ActionInput.Text, textBox_ActionOutput.Text, isSave);
            }
            // show tips if need
            if (errorMsg == null)
            {
                label_TokenTips.Text = "No Error";
                label_TokenTips.ForeColor = Color.Green;
            }
            else
            {
                label_TokenTips.Text = errorMsg;
                label_TokenTips.ForeColor = Color.Red;
            }
        }

        private void button_SaveActionInfo_Click(object sender, EventArgs e)
        {
            if (fsmDesigner.FocusedObject != null && fsmDesigner.FocusedObject is IRenderable)
            {
                IRenderable focusAction = fsmDesigner.FocusedObject as IRenderable;
                //TODO
                //focusAction.InnerAction.ApplyToken(textBox_ActionInput.Text, textBox_ActionTargetting.Text);
                if (listBox_tokenSeq.SelectedItem == null || !(listBox_tokenSeq.SelectedItem is ActionImpl))
                    return;
                // try to save action
                TryToSaveActionInfo(true);
                // try to parse for pair-wise
                /*try
                {
                    variableEditor_below.ParseVariables(textBox_ActionInput.Text);
                    variableEditor_below.ParseVariables(textBox_ActionOutput.Text);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }*/
            }
        }

        private void button_GenSuiteToFile_Click(object sender, EventArgs e)
        {
            using (SaveFileDialog svFileDlg = new SaveFileDialog())
            {
                svFileDlg.Filter = "C# files (*.cs)|*.cs";
                svFileDlg.FilterIndex = 0;
                svFileDlg.RestoreDirectory = true;

                StringBuilder tourMsgs = new StringBuilder();

                if (svFileDlg.ShowDialog() == DialogResult.OK)
                {
                    try
                    {
                        SuiteMaker maker = new SuiteMaker();                      

                        Dictionary<State, List<Tour>> entryTourMap = new Dictionary<State, List<Tour>>();
                        foreach (ListViewItem lvi in listView_GenSuite.CheckedItems)
                        {
                            TourListItem tli = lvi as TourListItem;
                            if (tli == null)
                                continue;
                            if(!entryTourMap.ContainsKey(tli.UITour.EntryR.InnerState))
                            {
                                entryTourMap.Add(tli.UITour.EntryR.InnerState, new List<Tour>());
                            }
                            entryTourMap[tli.UITour.EntryR.InnerState].Add(tli.UITour.InnerTour);
                        }
                        Logger.Instance.Info("Testcase group count : {0}", entryTourMap.Count);
                        // Only save selected test cases
                        foreach (State entry in entryTourMap.Keys)
                        {
                            Logger.Instance.Info("Generating cases for group with entry : {0}", entry.Id);
                            Logger.Instance.Info("Cases in this group: {0}", entryTourMap[entry].Count);
                            maker.AppendCaseGroup(entryTourMap[entry]);  
                        }
                        // Save tokens
                        foreach (TreeNode node in tokenRoot.Nodes)
                        {
                            maker.AppendToken(node.Tag as TokenImpl);
                        }
                        // Save functions
                        foreach (FunctionR method in FunctionNames)
                        {
                            Logger.Instance.Info("Generating stub for user-method: {0}", method);
                            FunctionImpl impl = TokenManager.GetDefinition(method.Name) as FunctionImpl;
                            maker.AppendFunction(impl);
                        }
                        // Save variables

                        // use variable editor to generate pair-wise output
                        variableEditor_below.UpdateVariables(GetVariableNames());
                        List<PictOutputVarList> outputList = variableEditor_below.GetPictOutput();
                        // change the output to List<Dictionary<varName,varValue>>
                        foreach (PictOutputVarList pl in outputList)
                        {
                            Dictionary<string, string> pMap = new Dictionary<string, string>();
                            foreach (PictOutputVariable pv in pl.Variables)
                            {
                                if (!pMap.ContainsKey(pv.Name))
                                    pMap.Add(pv.Name, pv.Value);
                            }
                            maker.AppendVarMap(pMap);
                        }

                        maker.Save(svFileDlg.FileName);
                        Logger.Instance.Info("suite saved to {0}", svFileDlg.FileName);
                    }
                    catch (Exception ex)
                    {
                        Logger.Instance.Error("Suite Generation failed, exception: {0}", ex.ToString());
                    }
                }                
            }
        }

        private const string defaultDesignerTempFile = "designer.fsm.gen.xml";

        private void propertyGrid_RenderItem_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            //check id duplicate
            if (e.ChangedItem.Label == "Id" && fsmDesigner.FocusedObject != null)
            {
                if (fsmDesigner.FocusedObject is StateR)
                {
                    State innerState = (fsmDesigner.FocusedObject as StateR).InnerState;
                    if (!IdGenerator.Instance.TryStateNewId(e.OldValue.ToString(), e.ChangedItem.Value.ToString()))
                    {
                        MessageBox.Show("This state ID is already in use, please try another", "Oops...");
                        innerState.Id = e.OldValue.ToString();
                    }
                }
                else if (fsmDesigner.FocusedObject is ActionR)
                {
                    Action innerAction = (fsmDesigner.FocusedObject as ActionR).InnerAction;
                    if(!IdGenerator.Instance.TryActionNewId(e.OldValue.ToString(), e.ChangedItem.Value.ToString()))
                    {
                        MessageBox.Show("This action ID is already in use, please try another", "Oops...");
                        innerAction.Id = e.OldValue.ToString();
                    }
                }
            }
            fsmDesigner.Refresh();
        }

        private void toolStripButton_Save_Click(object sender, EventArgs e)
        {
            using (SaveFileDialog svFileDlg = new SaveFileDialog())
            {
                svFileDlg.Filter = "xml files (*.xml)|*.xml|All files (*.*)|*.*";
                svFileDlg.FilterIndex = 0;
                svFileDlg.RestoreDirectory = true;

                if (svFileDlg.ShowDialog() == DialogResult.OK)
                {
                    try
                    {
                        MToolStudioData data = new MToolStudioData(fsmDesigner.RenderItems, FunctionNames , fsmDesigner.Machine);
                        foreach (TreeNode n in tokenRoot.Nodes)
                        {
                            data.Tokens.Add(n.Tag as TokenImpl);
                        }
                        data.SaveToFile(svFileDlg.FileName);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.ToString());
                    }
                }
            }
        }

        private void toolStripButton_Open_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog opFileDlg = new OpenFileDialog())
            {
                opFileDlg.Filter = "xml files (*.xml)|*.xml|All files (*.*)|*.*";
                if (opFileDlg.ShowDialog() == DialogResult.OK)
                {
                    try
                    {
                        MToolStudioData data = MToolStudioData.LoadFromFile(opFileDlg.FileName);
                        fsmDesigner.LoadFromStudioData(data);
                        IdGenerator.Instance.LoadFromStudioData(data);
                        TokenManager.Reset();
                        FunctionNames = data.Functions;

                        if (tokenRoot.Nodes.Count != 0) tokenRoot.Nodes.Clear();
                        foreach (TokenImpl t in data.Tokens)
                        {
                            TokenManager.SetDefinition(t.Name, t);
                            TreeNode n = new TreeNode(t.Name);
                            n.Tag = t;
                            tokenRoot.Nodes.Add(n);
                        }
                        tokenRoot.Expand();

                        //handle parameter combinations
                        /*variableEditor_below.Clear();
                        foreach (ActionR ar in data.Actions)
                        {
                            if (ar.InnerAction.Stubs != null)
                            {
                                foreach (ActionImpl ai in ar.InnerAction.Stubs)
                                {
                                    if (ai is TokenImpl)
                                    {
                                        TokenImpl ti = ai as TokenImpl;

                                        variableEditor_below.ParseVariables(ti.InputXMLClean);
                                        variableEditor_below.ParseVariables(ti.TargettingClean);
                                    }
                                    else if (ai is FunctionImpl)
                                    {
                                        FunctionImpl fi = ai as FunctionImpl;
                                        variableEditor_below.ParseVariables(fi.InputToString);
                                    }
                                    //TODO: automatically get parameters to variableEditor
                                }
                            }
                        }*/
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.ToString());
                    }
                }
            }
        }

        private void newToolStripButton_Click(object sender, EventArgs e)
        {
            fsmDesigner.Reset();
            IdGenerator.Instance.Reset();
        }

        private void button_GenCases_Click(object sender, EventArgs e)
        {
            List<List<Tour>> caseGroups = null;
            columnHeader_Case.Width = listView_GenSuite.Size.Width;
            listView_GenSuite.Items.Clear();
            try
            {
                //update the FSM xml first
                fsmDesigner.Machine.SaveToXML(tmpSaveFilePath);
                //use a new FSM to load the xml (thus it can initialize the relations between states/actions/tokens)
                FSM anotherFSM = FSM.LoadFromXML(tmpSaveFilePath);
                anotherFSM.Initialize();

                caseGroups = SuiteMaker.MakeSuite(anotherFSM, int.Parse(textBox_minStep.Text),
                  int.Parse(textBox_maxStep.Text), int.Parse(textBox_maxRepeat.Text));
            }
            catch (Exception ex)
            {
                MessageBox.Show(string.Format("Failed to generate cases:{0}", ex.ToString()));
            }
            if (caseGroups == null)
                return;
            Dictionary<string, IRenderable> uiMapping = fsmDesigner.GetUIMapping();
            foreach (List<Tour> tours in caseGroups)
            {
                foreach (Tour singleCase in tours)
                {
                    listView_GenSuite.Items.Add(new TourListItem(singleCase, uiMapping));
                }
            }
        }

        private void button_selectAll_Click(object sender, EventArgs e)
        {
            foreach (ListViewItem lvi in listView_GenSuite.Items)
                lvi.Checked = true;
        }

        private void button_selectNone_Click(object sender, EventArgs e)
        {
            foreach (ListViewItem lvi in listView_GenSuite.Items)
                lvi.Checked = false;
        }

        private void UpdateToursOnFSMDesigner()
        {
            fsmDesigner.FocusedTours = new List<TourR>();
            if (listView_GenSuite.SelectedItems.Count > 0)
            {
                List<TourR> tr = new List<TourR>();
                foreach (TourListItem tli in listView_GenSuite.SelectedItems)
                {
                    tr.Add(tli.UITour);
                }
                fsmDesigner.FocusedTours = tr;
                fsmDesigner.Refresh();
            }
        }

        private void listView_GenSuite_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateToursOnFSMDesigner();
            UpdateCaseToolStrip();
        }

        private void listView_GenSuite_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
        {
            UpdateToursOnFSMDesigner();
            UpdateCaseToolStrip();
        }

        private void UpdateCaseToolStrip()
        {
            if (listView_GenSuite.SelectedItems.Count > 1)
            {
                toolStripStatusLabel_caseInfo.Text = string.Format("{0} case(s) selected", listView_GenSuite.SelectedItems.Count);
            }
            else if (listView_GenSuite.SelectedItems.Count == 1)
            {
                TourR t = (listView_GenSuite.SelectedItems[0] as TourListItem).UITour as TourR;
                if (t == null)
                    return;
                int controlCount = 0;
                foreach (ActionR acr in t.ActionRs)
                    if (acr.InnerAction.Type == ActionType.Control)
                        controlCount++;
                toolStripStatusLabel_caseInfo.Text = string.Format("Entry State: {0}; Steps: {1} ({2} controls + {3} verifications)",
                    t.EntryR.InnerState.Id, t.ActionRs.Count, controlCount, t.ActionRs.Count - controlCount);
            }
        }

        private void MtoolMainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            fsmDesigner.CurrentTool = null;

            Logger.Instance.OnLogGrown -= new LogChangeHandler(Instance_OnLogGrown);
            fsmDesigner.OnCurrentUIToolChanged -= new EventHandler<UIToolEventArgs>(fsmDesigner_OnCurrentUIToolChanged);
            fsmDesigner.OnFocusObjectChanged -= new EventHandler<UIFocusChangedEventArgs>(fsmDesigner_OnFocusObjectChanged);
        }

        private void listView_GenSuite_ItemChecked(object sender, ItemCheckedEventArgs e)
        {
            toolStripStatusLabel_checkCaseInfo.Text = string.Format("{0} case(s) checked", listView_GenSuite.CheckedItems.Count);
        }

        private void tabControl_Main_KeyPress(object sender, KeyPressEventArgs e)
        {
        }

        private GridItem GetRootGrid(GridItem grid)
        {
            if (grid == null)
                return null;
            GridItem root = grid;
            while (root.Parent is GridItem)
            {
                root = root.Parent;
            }
            return root;
        }

        private GridItem GetItemByLabel(GridItem root, string label)
        {
            if (root == null)
                return null;
            if (root.Label.ToLower() == label.ToLower())
                return root; 
            if(root.GridItems == null)
                return null;
            foreach (GridItem item in root.GridItems)
            {
                GridItem candidate = GetItemByLabel(item, label);
                if (candidate != null)
                    return candidate;
            }
            return null;
        }

        private void tabControl_Main_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.F2)
            {
                tabControl_Below.SelectedTab = tabPage_FSMDetails; 
            }
            else if (e.KeyData == Keys.F3)
            {
                tabControl_Below.SelectedTab = tabPage_ViewSuite;
            }
            else if (e.Control && e.KeyCode == Keys.S)
            {
                button_SaveActionInfo_Click(null, null);
            }
        }

        private void listView_GenSuite_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.Escape)
            {
                listView_GenSuite.SelectedItems.Clear();
                fsmDesigner.Refresh();
            }
        }

        private void listBox_tokenSeq_SelectedIndexChanged(object sender, EventArgs e)
        {
            ActionImpl focusedStub = (listBox_tokenSeq.SelectedItem) as ActionImpl;
            if (focusedStub != null)
            {
                textBox_ActionInput.Enabled = true;
                textBox_ActionOutput.Enabled = true;
                textBox_ActionInput.Text = focusedStub.InParasText;
                textBox_ActionOutput.Text = focusedStub.OutParasText;
                // error parameter will not be saved, so can don't check here
                //TryToSaveActionInfo(false);
                if (focusedStub is TokenImpl)
                    groupBox_tokenDetails.Text = "Enter token parameter (User Ctrl-S to Save)";
                else if (focusedStub is FunctionImpl)
                    groupBox_tokenDetails.Text = "Enter function parameters (User Ctrl-S to Save)";                              
            }
        }

        /// <summary>
        /// swap lb[i1] and lb[i2], and lb will select i1 after that
        /// </summary>
        /// <param name="lb"></param>
        /// <param name="i1"></param>
        /// <param name="i2"></param>
        private static void swapListBoxItems(ListBox lb, int i1, int i2)
        {
            object tmp = lb.Items[i1];
            lb.BeginUpdate();
            lb.Items[i1] = lb.Items[i2];
            lb.Items[i2] = tmp;
            lb.SelectedIndex = i1;
            lb.EndUpdate();
        }

        private void moveTokenSeq(bool up)
        {
            if (listBox_tokenSeq.SelectedItem == null ||
                fsmDesigner.FocusedObject == null)
                return;
            ActionImpl ai = listBox_tokenSeq.SelectedItem as ActionImpl;
            IActionRenderable ar = fsmDesigner.FocusedObject as IActionRenderable;
            if (ai == null || ar == null)
                return;
            int index = listBox_tokenSeq.SelectedIndex;
            if (up)
            {
                if (index > 0)
                {
                    ar.InnerAction.MoveUp(index);
                    swapListBoxItems(listBox_tokenSeq, index - 1, index);
                }
            }
            else
            {
                if (index < listBox_tokenSeq.Items.Count - 1)
                {
                    ar.InnerAction.MoveUp(index + 1);
                    swapListBoxItems(listBox_tokenSeq, index + 1, index);
                }
            }
        }

        private void button_tokenSeq_up_Click(object sender, EventArgs e)
        {
            moveTokenSeq(true);
        }

        private void button_tokenSeq_down_Click(object sender, EventArgs e)
        {
            moveTokenSeq(false);
        }

        private void button_tokenSeqDel_Click(object sender, EventArgs e)
        {
            if (listBox_tokenSeq.SelectedItem == null ||
                fsmDesigner.FocusedObject == null)
                return;
            ActionImpl ai = listBox_tokenSeq.SelectedItem as ActionImpl;
            IActionRenderable ar = fsmDesigner.FocusedObject as IActionRenderable;
            if (ai == null || ar == null)
                return;

            ar.InnerAction.RemoveActionImpl(ai);
            listBox_tokenSeq.Items.Remove(listBox_tokenSeq.SelectedItem);
            if (listBox_tokenSeq.Items.Count > 0)
                listBox_tokenSeq.SelectedIndex = 0;
        }

        private void tabControl_Below_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (tabControl_Below.SelectedTab == tabPage_FSMDetails)
            {
                //remove the current hightlighted tours if there's any
                listView_GenSuite.SelectedItems.Clear();
                fsmDesigner.Refresh();
            }
            else if(tabControl_Below.SelectedTab == tabPage_Variables)
            {
                //parse parameter from actions
                variableEditor_below.UpdateVariables(GetVariableNames());
                variableEditor_below.UpdateListView();
            }
        }

        private List<string> GetVariableNames()
        {
            List<string> names = new List<string>();
            foreach (Action action in fsmDesigner.Machine.Actions)
            {
                if (action == null || action.Stubs == null) continue;
                foreach (ActionImpl impl in action.Stubs)
                {
                    if (impl.InParas == null) continue;
                    foreach (Parameter p in impl.InParas)
                    {
                        if (p.Var == null || !p.Var.IsVar) continue;
                        if (!names.Contains(p.Var.Name))
                            names.Add(p.Var.Name);
                    }
                }
            }
            return names;
        }

        private void treeView_Tokens_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Right)
                return;

            Console.WriteLine("treeView_Tokens_MouseClick");
            TreeNode tnd = treeView_Tokens.GetNodeAt(e.X, e.Y);

            if (tnd.Parent == tokenRoot)
                contextMenuStrip_DelTokenImpl.Show(treeView_Tokens.PointToScreen(new Point(e.X, e.Y)));
            else if (tnd.Parent == funcRoot)
                contextMenuStrip_DelFuncImpl.Show(treeView_Tokens.PointToScreen(new Point(e.X, e.Y)));
            else if (tnd != null && tnd.Tag != null)
            {
                if (tnd.Tag == typeof(FunctionImpl))
                    contextMenuStrip_AddFuncImpl.Show(treeView_Tokens.PointToScreen(new Point(e.X, e.Y)));
                else if (tnd.Tag == typeof(TokenImpl))
                    contextMenuStrip_AddTokenImpl.Show(treeView_Tokens.PointToScreen(new Point(e.X, e.Y)));
            }
        }

        private void toolStripTextBox_AddFuncImpl_Enter(object sender, EventArgs e)
        {
            contextMenuStrip_AddFuncImpl.Hide();
        }

        private void toolStripTextBox_AddFuncImpl_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Escape)
            {
                toolStripTextBox_AddFuncImpl.Text = "";
                contextMenuStrip_AddFuncImpl.Hide();
            }
            else if (e.KeyCode == Keys.Enter)
            {
                string funcName = toolStripTextBox_AddFuncImpl.Text;
                toolStripTextBox_AddFuncImpl.Text = "";
                contextMenuStrip_AddFuncImpl.Hide();
                if (TokenManager.GetDefinition(funcName) != null)
                    MessageBox.Show(string.Format("Token/Function '{0}' has been defined.", funcName));
                else
                    AddNewFunctionImpl(funcName);
            }
        }

        #region FUNCTION RELATED

        private static int funcPosCounter = 0;
        private static int funcPosXOffset = 30;
        private static int funcPosYOffset = 20;
        private static int funcPosYStep = 30;
        private List<FunctionR> userFunctions = new List<FunctionR>();

        private void AddNewFunctionImpl(string funcName)
        {
            FunctionImpl newFunc = new FunctionImpl();
            newFunc.Name = funcName;
            TokenManager.SetDefinition(funcName, newFunc);
            TreeNode newNode = funcRoot.Nodes.Add(funcName, funcName);
            newNode.Tag = newFunc;
            funcRoot.Expand();

            FunctionR fr = new FunctionR(new Point(funcPosXOffset,
                funcPosYOffset + funcPosYStep * funcPosCounter), funcName);
            userFunctions.Add(fr);
            newFunc.InnerAction = fr.InnerAction;
            fsmDesigner.RenderItems.Add(fr);
            funcPosCounter++;
            fsmDesigner.Refresh();
        }

        private List<FunctionR> FunctionNames
        {
            get 
            {
                //List<string> retv = new List<string>();
                //foreach (TreeNode nod in funcRoot.Nodes)
                //    retv.Add(nod.Name);
                //return retv;
                return userFunctions;
            }
            set
            {
                userFunctions = value;
                funcRoot.Nodes.Clear();
                //userFunctions.Clear();
                foreach (FunctionR func in value)
                {
                    FunctionImpl newFunc = new FunctionImpl();
                    newFunc.Name = func.Name;
                    newFunc.InnerAction = func.InnerAction;
                    TokenManager.SetDefinition(func.Name, newFunc);
                    TreeNode newNode = funcRoot.Nodes.Add(func.Name, func.Name);
                    newNode.Tag = newFunc;
                    fsmDesigner.RenderItems.Add(func);
                    //AddNewFunctionImpl(func);
                }
                funcRoot.Expand();
                if (userFunctions.Count > 0)
                    fsmDesigner.Refresh();
            }
        }

        #endregion

        private void listBox_tokenSeq_MouseUp(object sender, MouseEventArgs e)
        {
            if (fsmDesigner.CurrentTool != null && fsmDesigner.CurrentTool is ActionImplApplyTool)
                fsmDesigner.CurrentTool.OnMouseUp(this, e);
            Cursor = Cursors.Default;
        }

        private void listBox_tokenSeq_MouseEnter(object sender, EventArgs e)
        {
            ActionImplApplyTool actualTool = fsmDesigner.CurrentTool as ActionImplApplyTool;
            if (actualTool != null)
            {
                actualTool.MouseInsideActionListBox = true;
                Cursor = Cursors.PanNE;
            }            
        }

        private void listBox_tokenSeq_MouseLeave(object sender, EventArgs e)
        {
            ActionImplApplyTool actualTool = fsmDesigner.CurrentTool as ActionImplApplyTool;
            if (actualTool != null)
                actualTool.MouseInsideActionListBox = false;
            Cursor = Cursors.Default;
        }

        private void fsmDesigner_OnCharInputFocusedObject(object sender, KeyEventArgs e)
        {
            if (fsmDesigner.FocusedObject != null)
            {
                if (e.KeyCode == Keys.F2)
                {
                    propertyGrid_RenderItem.Focus();
                    //propertyGrid_RenderItem.Select();
                    GridItem idGrid = GetItemByLabel(GetRootGrid(propertyGrid_RenderItem.SelectedGridItem), "id");
                    
                    if (idGrid != null)
                    {
                        idGrid.Select();
                        Logger.Instance.Info("[GRIDITEM]" + idGrid.Label);
                    }
                }
            }
        }

        private void MtoolMainForm_Scroll(object sender, ScrollEventArgs e)
        {

        }

        private void menuitem_addtoken_Click(object sender, EventArgs e)
        {
            using(ModifyToken mtForm = new ModifyToken())
            {
                mtForm.TokenRoot = tokenRoot;
                mtForm.ShowDialog();
            }
        }

        private void contextMenuStrip_AddFuncImpl_Opening(object sender, CancelEventArgs e)
        {

        }

        private void treeview_Token_DoubleClick(object sender, EventArgs e)
        {
            TreeView view = sender as TreeView;
            if (view == null || view.SelectedNode == null) return;

            TokenImpl impl = view.SelectedNode.Tag as TokenImpl;
            if (impl == null) return;

            using (ModifyToken mtForm = new ModifyToken())
            {
                mtForm.TokenRoot = tokenRoot;
                mtForm.Token = impl;
                mtForm.ShowDialog();
            }
        }

        private void menuitem_delfunction_Click(object sender, EventArgs e)
        {
            TreeNode node = treeView_Tokens.SelectedNode;
            FunctionImpl impl = node.Tag as FunctionImpl;
            if (impl == null) return;

            TokenManager.DeleteDefinition(impl.Name);
            foreach (FunctionR fr in FunctionNames)
            {
                if (fr.Name != impl.Name) continue;
                FunctionNames.Remove(fr);
                fsmDesigner.RenderItems.Remove(fr);
                fsmDesigner.Refresh();
                break;
            }
            node.Remove();
        }

        private void menuitem_deltoken_Click(object sender, EventArgs e)
        {
            TreeNode node = treeView_Tokens.SelectedNode;
            TokenImpl impl = node.Tag as TokenImpl;
            if (impl == null) return;

            TokenManager.DeleteDefinition(impl.Name);
            node.Remove();
        }

        //private void fsmDesigner_KeyPress(object sender, KeyPressEventArgs e)
        //{
        //    if ((e.KeyChar >= 'a' && e.KeyChar <= 'z') && (e.KeyChar >= 'A' && e.KeyChar <= 'Z'))
        //    {
        //        foreach (GridItem gi in propertyGrid_RenderItem.SelectedGridItem.GridItems)
        //        {
        //            Logger.Instance.Info("[GRIDITEM]"+gi.Value);
        //        }
        //    }
        //}

        //private void fsmDesigner_KeyDown(object sender, KeyEventArgs e)
        //{
        //    if (e.KeyCode >= Keys.A && e.KeyCode <= Keys.Z)
        //    {
        //        foreach (GridItem gi in propertyGrid_RenderItem.SelectedGridItem.GridItems)
        //        {
        //            Logger.Instance.Info("[GRIDITEM]" + gi.Value);
        //        }
        //    }
        //}
    }
}
